﻿#if NETFX_CORE && !WinRT
#	define WinRT
#endif

    using System;

#if WinRT
    using Windows.ApplicationModel;
    using Windows.UI.Core;
    using Windows.UI.Xaml;
#else
using System.Windows.Threading;
#endif
#if !SILVERLIGHT || SL5 || WP8
	using System.Threading;
    using System.Threading.Tasks;
#endif

namespace DailyFx.UI
{

	/// <summary>
	///   Enables easy marshalling of code to the UI/Background thread.
	/// </summary>
	public static class Execution
	{
#if WinRT
        static CoreDispatcher _dispatcher;
#else
		static Dispatcher _dispatcher;
#endif
		static Action<System.Action> _delegation = action => action();

		

		/// <summary>
		///   Initializes the framework using the current dispatcher.
		/// </summary>
		public static void SetUIThreadDirector ( bool useDispather = true, Action<System.Action> delegetion = null )
		{
			if ( useDispather )
			{
#if SILVERLIGHT
				_dispatcher = System.Windows.Deployment.Current.Dispatcher;
#elif WinRT
				_dispatcher = Window.Current.Dispatcher;
#else
				_dispatcher = Dispatcher.CurrentDispatcher;
#endif
				_delegation = null;
			}
			else
			{
				_delegation = delegetion ?? ( action => action() );
				_dispatcher = null;
			}

		}

		/// <summary>
		///		Runs the action on the UI thread directly .
		/// </summary>
		/// <param name = "action">The action to execute.</param>
		public static void OnUIThread ( this System.Action action )
		{
			if ( _delegation != null )
			{
				_delegation ( action );
			}
#if WinRT
            else if ( dispatcher == null || Window.Current != null )
#else
			else if (_dispatcher == null || _dispatcher.CheckAccess() )
#endif
			{
				// directly call on ui thread .
				action ();
			}
			else
			{
#if !SILVERLIGHT || SL5 || WP8
				OnUIThreadEx (action).Wait(); // block .
#else
            
                var wait = new System.Threading.ManualResetEvent( false );
                Exception exception = null;
                ToUIThread ( () => 
				{
                    try 
					{
                        action();
                    }
                    catch ( Exception ex )
					{
                        exception = ex;
                    }
                    wait.Set();
                });

                wait.WaitOne();
                if ( exception != null )
				{
                    throw new TargetInvocationException (
						"Error occurred while dispatching to the UI Thread", exception ); 
				}
#endif
			}
		}

		/// <summary>
		///		 Posts the action on the UI thread asynchronously from other thread .
		/// </summary>
		public static void ToUIThread ( this System.Action action )
		{
			DailyFx.Common.Check.MustValid( _dispatcher != null, " Not initialized with dispather" );
#if WinRT
            var dummy = dispatcher.RunAsync( CoreDispatcherPriority.Normal, () => action() );
#else
			_dispatcher.BeginInvoke ( action );
#endif
		}

#if !SILVERLIGHT || SL5 || WP8
		/// <summary>
		///		Runs the action on the UI thread asynchronously, task is awaitable for other thread .
		/// </summary>
		public static Task OnUIThreadEx ( this System.Action action )
		{
#if WinRT
            return _dispatcher.RunAsync ( CoreDispatcherPriority.Normal, () => action()).AsTask();
#elif NET45
			return _dispatcher.InvokeAsync(action).Task;
#else
            var taskSource = new TaskCompletionSource<object>();
            System.Action method = () => 
			{
                try {
                    action();
                    taskSource.SetResult(null);
                }
                catch(Exception ex) {
                    taskSource.SetException(ex);
                }
            };

            _dispatcher.BeginInvoke(method);
            return taskSource.Task;
#endif
		}
#endif

#if NET45 // todo:

		public static void ToUIThread <T> ( Action<T> method, T argument, 
			DispatcherPriority priority = DispatcherPriority.Normal )
		{
			_dispatcher.BeginInvoke( method, priority, argument );
		}
#endif


		public static void OnBackground ( Action method )
		{
			ThreadPool.QueueUserWorkItem((x) => method() );
		}

		public static void OnBackground<T> ( Action<T> method, T parameter)
		{
			ThreadPool.QueueUserWorkItem( (x) => method(parameter) );
		}


	}
}